// 
//  Copyright © 2009 Jiří Zárevúcky <zarevucky.jiri@gmail.com>
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as
//  published by the Free Software Foundation, either version 3 of the
//  License, or (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
// 
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// 


using System;
using System.Collections.Generic;

using Galaxium.Protocol.Xmpp.Library.Core;
using Galaxium.Protocol.Xmpp.Library.Xml;


namespace Galaxium.Protocol.Xmpp.Library.Extensions
{
	public class vCard
	{
		public vCard (Client client, JabberID jid)
		{
			_client = client;
			_jid = jid;
		}
		
		private Client _client;
		private JabberID _jid;
		
		public event EventHandler Finished;
		public event EventHandler<StanzaErrorEventArgs> Failed;
		
		protected void OnFinished ()
		{
			if (Finished != null)
				Finished (this, EventArgs.Empty);
		}
		
		protected void OnFailed (StanzaError error)
		{
			if (Failed != null)
				Failed (this, new StanzaErrorEventArgs (error));
		}
		
		public void Retrieve ()
		{
			var iq = new Iq (IqType.Get, "vCard", Namespaces.vCard);
			iq.To = _jid;
			_client.SendQuery (iq, HandleGetResult);
		}
		
		private void HandleGetResult (Iq result)
		{
			if (result == null)
				OnFailed (null);
			else if (result.IsError)
				OnFailed (result.Error);
			else {
				Decode (result.Query ?? new Element ("vCard"));
				OnFinished ();
			}
		}
		
		public void Set ()
		{
			var iq = new Iq (IqType.Set);
			iq.Query = ToXml ();
			_client.SendQuery (iq, HandleSetResult);
		}
		
		private void HandleSetResult (Iq result)
		{
			if (result == null)
				OnFailed (null);
			if (result.IsError)
				OnFailed (result.Error);
			else
				OnFinished ();
		}
		
		public string FullName { get; set; }
		public string FamilyName { get; set; }
		public string GivenName { get; set; }
		public string MiddleName { get; set; }
		public string NamePrefix { get; set; }
		public string NameSuffix { get; set; }
		public string Nickname { get; set; }
		
		public DateTime Birthday { get; set; }
		
		public string PhotoType { get; set; }
		public string PhotoData { get; set; }

		public vCardAddress Address { get; set; }
		public string Phone { get; set; }
		public string Email { get; set; }
				
		public string Title { get; set; }
		public string Role { get; set; }
		public string Organization { get; set; }
		
		public string ProductID { get; set; }
		public DateTime Revision { get; set; }
				
		public string URL { get; set; }
				
		public string Description { get; set; }

		protected void Decode (Element vcard)
		{
			FullName = vcard.GetTextChild ("FN", null);
			
			var name = vcard.FirstChild ("N", null);
			if (name == null)
				FamilyName = GivenName = MiddleName = NamePrefix = NameSuffix = null;
			else {
				FamilyName = name.GetTextChild ("FAMILY", null);
				GivenName = name.GetTextChild ("GIVEN", null);
				MiddleName = name.GetTextChild ("MIDDLE", null);
				NamePrefix = name.GetTextChild ("PREFIX", null);
				NameSuffix = name.GetTextChild ("SUFFIX", null);
			}
			
			Nickname = vcard.GetTextChild ("NICKNAME", null);
			
			var photo = vcard.FirstChild ("PHOTO", null);
			if (photo != null) {
				PhotoType = photo.GetTextChild ("TYPE", null);
				PhotoData = photo.GetTextChild ("BINVAL", null);
			}
			
			try { Birthday = DateTime.Parse (vcard.GetTextChild ("BDAY", null)); }
			catch { Birthday = DateTime.MinValue; }
			
			var addr_elm = vcard.FirstChild ("ADR", null);
			Address = addr_elm == null ? new vCardAddress () : new vCardAddress (addr_elm);

			var phone_elm = vcard.FirstChild ("TEL", null);
			Phone = (phone_elm == null) ? null : phone_elm.GetTextChild ("NUMBER", null);
			
			var mail_elm = vcard.FirstChild ("EMAIL", null);
			Email = (mail_elm == null) ? null : mail_elm.GetTextChild ("USERID", null);
			
			Title = vcard.GetTextChild ("TITLE", null);
			Role = vcard.GetTextChild ("ROLE", null);
			
			var org_elm = vcard.FirstChild ("ORG", null);
			if (org_elm != null)
				Organization = org_elm.GetTextChild ("ORGNAME", null);
			
			ProductID = vcard.GetTextChild ("PRODID", null);
			try { Revision = DateTime.Parse (vcard.GetTextChild ("REV", null)); }
			catch { Revision = DateTime.MinValue; }
			
			URL = vcard.GetTextChild ("URL", null);
			
			Description = vcard.GetTextChild ("DESC", null);
		}
		
		protected Element ToXml ()
		{
			var vcard = new Element ("vCard", Namespaces.vCard);
			
			if (!String.IsNullOrEmpty (FullName))
				vcard.AppendTextChild ("FN", null, FullName);
			
			var name = new Element ("N");
			if (!String.IsNullOrEmpty (FamilyName))
				name.AppendTextChild ("FAMILY", null, FamilyName);
			if (!String.IsNullOrEmpty (GivenName))
				name.AppendTextChild ("GIVEN", null, GivenName);
			if (!String.IsNullOrEmpty (MiddleName))
				name.AppendTextChild ("MIDDLE", null, MiddleName);
			if (!String.IsNullOrEmpty (NamePrefix))
				name.AppendTextChild ("PREFIX", null, NamePrefix);
			if (!String.IsNullOrEmpty (NameSuffix))
				name.AppendTextChild ("SUFFIX", null, NameSuffix);
			vcard.AppendChild (name);

			if (!String.IsNullOrEmpty (Nickname))
				vcard.AppendTextChild ("NICKNAME", null, Nickname);
			
			if (!String.IsNullOrEmpty (PhotoData)) {
				var photo = new Element ("PHOTO");
				if (!String.IsNullOrEmpty (PhotoType))
					photo.AppendTextChild ("TYPE", null, PhotoType);
				photo.AppendTextChild ("BINVAL", null, PhotoData);
				vcard.AppendChild (photo);
			}
			
			if (Birthday != DateTime.MinValue)
				vcard.AppendTextChild ("BDAY", null, Birthday.ToString ("yyyy-MM-dd"));
			
			if (Address != null)
				vcard.AppendChild (Address.ToXml ());

			if (!String.IsNullOrEmpty (Phone)) {
				var phone_elm = new Element ("TEL");
				phone_elm.AppendTextChild ("NUMBER", null, Phone);
				vcard.AppendChild (phone_elm);
			}
			
			if (!String.IsNullOrEmpty (Email)) {
				var mail_elm = new Element ("EMAIL");
				mail_elm.AppendTextChild ("USERID", null, Email);
				vcard.AppendChild (mail_elm);
			}
			
			if (!String.IsNullOrEmpty (Title))
				vcard.AppendTextChild ("TITLE", null, Title);
			if (!String.IsNullOrEmpty (Role))
				vcard.AppendTextChild ("ROLE", null, Role);
			
			if (!String.IsNullOrEmpty (Organization)) {
				var org_elm = new Element ("ORG");
				org_elm.AppendTextChild ("ORGNAME", null, Organization);
				vcard.AppendChild (org_elm);
			} 
			
			if (!String.IsNullOrEmpty (ProductID))
				vcard.AppendTextChild ("PRODID", null, ProductID);
			
			vcard.AppendTextChild ("REV", null,
			                       DateTime.UtcNow.ToString ("yyyy-MM-dd'T'HH':'mm':'ss'Z'"));

			if (!String.IsNullOrEmpty (URL))
				vcard.AppendTextChild ("URL", null, URL);
			if (!String.IsNullOrEmpty (Description))
				vcard.AppendTextChild ("DESC", null, Description);
			
			return vcard;
		}
		
		public DataForm ToForm ()
		{
			Action<DataFormSubmitArgs> submit = (args) => {
				FullName = args.Form ["fullname"].GetString ();
				GivenName = args.Form ["givenname"].GetString ();
				FamilyName = args.Form ["familyname"].GetString ();
				Nickname = args.Form ["nickname"].GetString ();
				Phone = args.Form ["phone"].GetString ();
				Email = args.Form ["email"].GetString ();
				URL = args.Form ["url"].GetString ();
				Address.Locality = args.Form ["locality"].GetString ();
				Address.Street = args.Form ["street"].GetString ();
				Address.ExtendedAddress = args.Form ["extended"].GetString ();
				Address.Region = args.Form ["region"].GetString ();
				Address.PostCode = args.Form ["pcode"].GetString ();
				Address.Country = args.Form ["country"].GetString ();
				Organization = args.Form ["org"].GetString ();
				Title = args.Form ["title"].GetString ();
				Role = args.Form ["role"].GetString ();
				Description = args.Form ["desc"].GetString ();
				try { Birthday = DateTime.Parse (args.Form ["birthday"].GetString ()); }
				catch {}
				Set ();
			};
			
			var form = new DataForm (_jid == null ? FormAction.Form : FormAction.Result, submit);
			form.Title = "vCard for user " + _jid;
			form.AddSection ("General information");
			form.AddField ("fullname", FullName, "Full name");
			form.AddField ("givenname", GivenName, "Given name");
			form.AddField ("familyname", FamilyName, "Family name");
			form.AddFixedField (String.Empty);
			form.AddField ("nickname", Nickname, "Nickname");
			form.AddField ("birthday", Birthday == DateTime.MinValue ?
			               String.Empty : Birthday.ToString (), "Birthday");
			form.AddField ("phone", Phone, "Phone number");
			form.AddField ("email", Email, "E-Mail");
			form.AddField ("url", URL, "Homepage");
			form.AddSection ("Address");
			form.AddField ("locality", Address.Locality, "Locality");
			form.AddField ("street", Address.Street, "Street");
			form.AddField ("extended", Address.ExtendedAddress, String.Empty);
			form.AddField ("region", Address.Region, "Region");
			form.AddField ("pcode", Address.PostCode, "Post Code");
			form.AddField ("country", Address.Country, "Country");
			form.AddSection ("Work");
			form.AddField ("org", Organization, "Organization");
			form.AddField ("title", Title, "Title");
			form.AddField ("role", Role, "Role");
			form.AddSection ("About me");
			form.AddField ("desc", Description, String.Empty, true);
			return form;
		}
	}
}
